package base.util;

import base.standard.constant.ErrorCode;
import base.standard.exception.SimpleException;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.BeanUtils;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.ReflectionUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Bean util
 *
 * @description:
 * @author: cyj
 * @date 2020 -11-27 17:14:16
 */
@Slf4j
public class BeanUtil {

    /**
     * applicationContext
     */
    private static ConfigurableApplicationContext applicationContext;

    /**
     * Get bean t 定义一个获取已经实例化bean的方法
     *
     * @param <T>    parameter
     * @param tClass t class
     * @return the t
     * @description:
     */
    public static <T> T getBean(Class<T> tClass){
        return applicationContext.getBean(tClass);
    }

    /**
     * New instance t 创建类
     *
     * @param <T>       parameter
     * @param tClass    t class
     * @param paramType param type
     * @param params    params
     * @return the t
     */
    public static <T> T newInstance(Class<T> tClass, Class<?>[] paramType, Object[] params){
        try {
            return tClass.getConstructor(paramType).newInstance(params);
        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Sets application context * @description:
     *
     * @param applicationContext application context
     */
    public static void setApplicationContext(ConfigurableApplicationContext applicationContext) {
        BeanUtil.applicationContext = applicationContext;
    }

    /**
     * Spring invoke object 单个参数方法获取针对唯一校验器使用
     *
     * @param <T>        parameter
     * @param tClass     t class
     * @param methodName method name
     * @param param      param
     * @return the object
     * @description:
     */
    public static <T>  Object springInvoke(Class<T> tClass, String methodName, Object param) {
        T bean = getBean(tClass);
        Method method = ReflectionUtils.findMethod(bean.getClass(), methodName, param.getClass());
        Object result = null;
        try {
            result = method.invoke(bean, param);
        } catch (NullPointerException | IllegalAccessException | InvocationTargetException e) {
            log.info("spring invoke error.");
        }
        return result;
    }

    /**
     * New instance and copy t
     *
     * @param <T>    parameter
     * @param source source
     * @param target target
     * @return the t
     * @description: 将源对象转换到target对象 空不忽略
     */
    public static <T> T newInstanceAndCopy(Object source, Class<T> target) {
        if (null == source) {
            return null;
        }
        try {
            T object = target.newInstance();
            BeanUtils.copyProperties(source, object);
            return object;
        } catch (InstantiationException | IllegalAccessException e) {
            log.error("can't new obj and copy");
            throw new SimpleException(ErrorCode.UNKNOW_ERROR);
        }
    }

    /**
     * New instance and copy t
     *
     * @param <T>    parameter
     * @param source source
     * @param target target
     * @return the t
     * @description: 将源对象转换到target对象 空忽略
     */
    public static <T> T newInstanceAndCopyIgnoreNull(Object source, Class<T> target) {
        if (null == source) {
            return null;
        }
        try {
            T object = target.newInstance();
            BeanUtils.copyProperties(source, object, getNullArgs(source));
            return object;
        } catch (InstantiationException | IllegalAccessException e) {
            log.error("can't new obj and copy");
            throw new SimpleException(ErrorCode.UNKNOW_ERROR);
        }
    }

    /**
     * New instance and copy batch list
     *
     * @param <T>    parameter
     * @param source source
     * @param target target
     * @return the list
     * @description: 批量将源对象转换到target对象 空不忽略
     */
    public static <T> List<T> newInstanceAndCopyBatch(List<?> source, Class<T> target) {
        List<T> list = new LinkedList<>();
        source.stream().forEach(object -> list.add(newInstanceAndCopy(object, target)));
        return list;
    }

    /**
     * New instance and copy batch ignore null list
     *
     * @param <T>    parameter
     * @param source source
     * @param target target
     * @return the list
     * @description: 批量将源对象转换到target对象 空不忽略
     */
    public static <T> List<T> newInstanceAndCopyBatchIgnoreNull(List<?> source, Class<T> target) {
        List<T> list = new LinkedList<>();
        source.stream().forEach(object -> list.add(newInstanceAndCopyIgnoreNull(object, target)));
        return list;
    }

    /**
     * New instance and copy batch page
     *
     * @param <T>    parameter
     * @param source source
     * @param target target
     * @return the page
     * @description:
     */
    public static <T> IPage<T> newInstanceAndCopyBatch(IPage<?> source, Class<T> target) {
        Page<T> page = newInstanceAndCopy(source, Page.class);
        page.setRecords(newInstanceAndCopyBatch(source.getRecords(), target));
        return page;
    }

    /**
     * Get null args string [ ]
     *
     * @param object object
     * @return the string [ ]
     * @description: 获取null字段名
     */
    public static String[] getNullArgs(Object object) {
        if (null == object) {
            return null;
        }
        PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(object.getClass());
        List<String> nullArgs = Arrays.asList(propertyDescriptors).stream().filter(propertyDescriptor -> {
            Method readMethod = propertyDescriptor.getReadMethod();
            Object value = null;
            try {
                value = readMethod.invoke(object);
            } catch (IllegalAccessException | InvocationTargetException e) {
            }
            return null == value;
        }).map(propertyDescriptor -> propertyDescriptor.getName()).collect(Collectors.toList());
        return (String[]) nullArgs.toArray(new String[nullArgs.size()]);
    }

    /**
     * Gets method *
     *
     * @param joinPoint join point
     * @return the method
     * @description: 切面获取方法
     */
    public static Method getMethod(JoinPoint joinPoint) {
        return ((MethodSignature) joinPoint.getSignature()).getMethod();
    }
}
